package joe.works.pluginframework.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

import joe.works.pluginframework.AbstractPluginManager;
import joe.works.pluginframework.IExtensionPoint;
import joe.works.pluginframework.IPlugin;
import joe.works.pluginframework.IPluginManager;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.Ordered;

/**
 * Spring based implementation of {@link IPluginManager}.
 * @author josseyj
 *
 */
public class PluginManager extends AbstractPluginManager implements ApplicationContextAware, IPluginManager {
	
	private String[] pluginResources;
	private ApplicationContext beanFactory;
	private ApplicationContext parentFactory;
	/**
	 * @param pluginResources spring context xmls for the plugins 
	 */
	public void setPluginResources(String[] pluginResources) {
		this.pluginResources = pluginResources;
	}
	
	public void initialize() {
		if(pluginResources != null) {
			beanFactory = new ClassPathXmlApplicationContext(pluginResources, parentFactory);
		}
	}
	

	/**
	 * See {@link AbstractPluginManager#collectPlugins(IExtensionPoint)} 
	 * @param extensionPoint
	 * @return
	 */
	protected List<IPlugin> collectPlugins(IExtensionPoint extensionPoint) {
		ArrayList<IPlugin> plugins = new ArrayList<IPlugin>();
		Class<? extends IPlugin> pluginType = extensionPoint.getPluginType();
		Map<String, IPlugin> beansOfType = beanFactory.getBeansOfType(pluginType);
		for(IPlugin plugin: beansOfType.values()) {
			if(extensionPoint.getName().equals(plugin.getExtensionPointName()) && extensionPoint.isPlugable(plugin)) {
				plugins.add(plugin);
			}
		}
		Collections.sort(plugins, PluginOrderComparator.instance);
		return plugins;
	}

	public void setApplicationContext(ApplicationContext beanFactory) throws BeansException {
		this.parentFactory = beanFactory;
	}
	
	private static class PluginOrderComparator implements Comparator<IPlugin> {
		public static PluginOrderComparator instance = new PluginOrderComparator();

		private boolean isOrderSpecified(IPlugin plugin) {
			if(plugin instanceof Ordered) {
				return ((Ordered)plugin).getOrder() >= 0;
			}
			return false;
		}
		
		public int compare(IPlugin o1, IPlugin o2) {
			if(isOrderSpecified(o1) && isOrderSpecified(o2)) {
				return ((Ordered)o1).getOrder() - ((Ordered)o2).getOrder();
			}
			if(isOrderSpecified(o1)) {
				return -1;
			}
			if(isOrderSpecified(o2)) {
				return 1;
			}
			return 0;
		}
		
	}
}
